package com.haohuo.filter;

import com.alibaba.fastjson.JSON;
import com.haohuo.config.constans.TraceConstans;
import com.haohuo.filter.model.TraceRequest;
import com.haohuo.util.DateTimeTraceUtil;
import com.haohuo.util.TraceIDUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.util.*;

/**
 * @Author: pk.zhang
 * @Date: 2021/3/3 16:24
 */
public class MutableHttpServletRequest extends HttpServletRequestWrapper {

    public static final Logger logger = LoggerFactory.getLogger(MutableHttpServletRequest.class);

    // holds custom header and value mapping
    private final Map<String, String> customHeaders;

    byte[] body; // 报文

    public MutableHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        this.customHeaders = new HashMap<>();
        body = StreamUtils.copyToByteArray(request.getInputStream());
        try{
            String traceId = request.getHeader(TraceConstans.TRACEID_KEY);
            if (StringUtils.isEmpty(traceId)) {
                traceId = String.valueOf(TraceIDUtil.incrementAndGet());
                putHeader(TraceConstans.TRACEID_KEY, traceId);
            }
            // 设置MDC
            MDC.put(TraceConstans.TRACEID_KEY, traceId);

            String bodystr = new String(body);
            String ip = request.getHeader("x-forwarded-for");
            if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("PRoxy-Client-IP");
            }
            if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            logger.info("调用者IP地址:{}",ip);
            logger.info("RequestURI:{}",request.getRequestURI());
            logger.info("QueryString:{}",request.getQueryString());
            logger.info("RequestBody:{}",bodystr);

            TraceRequest traceRequest = new TraceRequest();
            traceRequest.setTraceID(traceId);

            // 获取attribute
            Map<String, Object> attributeMap = new HashMap<String, Object>();
            Enumeration<String> attributeNames = request.getAttributeNames();
            while (attributeNames != null && attributeNames.hasMoreElements()) {
                String attributeName = attributeNames.nextElement();
                Object attribute = request.getAttribute(attributeName);
                attributeMap.put(attributeName, attribute);
            }
            traceRequest.setAttributes(attributeMap);
            traceRequest.setRemoteAddress(new InetSocketAddress(ip, request.getRemotePort()));

            traceRequest.setStartTime(DateTimeTraceUtil.getNowDateTime());
            traceRequest.setReqJson(bodystr);

            MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (parameterMap != null && parameterMap.size() > 0) {
                parameterMap.entrySet().forEach(e -> {
                    String[] value = e.getValue();
                    if (value != null && value.length > 0) {
                        StringBuilder sb = new StringBuilder();
                        for (String v : value) {
                            sb.append(v);
                        }
                        queryParams.add(e.getKey(), sb.toString());
                    }
                });
            }

            traceRequest.setQueryParams(queryParams);

            HttpHeaders headers = new HttpHeaders();
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames != null && headerNames.hasMoreElements()) {
                String hkey = headerNames.nextElement();
                String hvalue = request.getHeader(hkey);
                headers.set(hkey, hvalue);
            }

            MultiValueMap<String, HttpCookie> cookieMap = new LinkedMultiValueMap<>();
            Cookie[] requestCookieList = request.getCookies();
            if (requestCookieList != null && requestCookieList.length > 0) {
                for (Cookie cookie : requestCookieList) {
                    cookieMap.add(cookie.getName(), new HttpCookie(cookie.getName(), cookie.getValue()));
                }
            }
            traceRequest.setCookies(cookieMap);
            traceRequest.setHeaders(headers);
            traceRequest.setPath(request.getRequestURI());

            logger.debug("tracerequest log:{}", JSON.toJSONString(traceRequest));

        }catch (Exception e){

        }


    }

    public void putHeader(String name, String value){
        this.customHeaders.put(name, value);
    }

    @Override
    public String getHeader(String name) {
        // check the custom headers first
        String headerValue = customHeaders.get(name);

        if (headerValue != null){
            return headerValue;
        }
        // else return from into the original wrapped object
        return ((HttpServletRequest) getRequest()).getHeader(name);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        // create a set of the custom header names
        Set<String> set = new HashSet<String>(customHeaders.keySet());

        // now add the headers from the wrapped request object
        @SuppressWarnings("unchecked")
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            // add the names of the request headers into the list
            String n = e.nextElement();
            set.add(n);
        }

        // create an enumeration from the set and return
        return Collections.enumeration(set);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

}
